﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using Aleator.Resources;


namespace Aleator.Core
{
    /// <summary>
    /// Partial class to hold chord list
    /// </summary>
    public partial class ConcreteProgression : Progression
    {
        #region Members

        private static XmlDocument m_doc;
        private static XmlNode m_root;
        private static XmlNodeList m_melodyList;

        private XmlNodeList m_clusterList;

        private Composition m_composition;
        private Dictionary<int, Chord> m_chords;

        private DrumPhrase m_drumphrase;
        private BassPhrase m_bassPhrase;
        private RhythmPhrase m_rhythmPhrase;
        private MelodyPhrase m_melodyPhrase;

        private int m_totalBeats;

        private List<ConcreteNote> m_notes;
        private List<Melody> m_melodies;

        private int m_currentMeasure;
        private int m_beatsPerCycle;

        private int m_sequenceId;

        #endregion

        #region Constructor

        /// <summary>
        /// Default constructor
        /// </summary>
        public ConcreteProgression()
        {
            Notes = new List<ConcreteNote>();
            Melodies = new List<Melody>();

            if (m_doc == null)
            {
                m_doc = new XmlDocument();

                m_doc.Load(Aleator.Resources.AppResources.MelodyDocumentPath);
                m_root = m_doc.SelectSingleNode("/Melodies");
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Load all of the melodies associated with this progression
        /// </summary>
        public override void LoadMelodies()
        {
            int beats = 0;
            byte melodyid;

            string melodystring = string.Format("//Melody[@CompositionID='{0}' and @SequenceID='{1}']", CompositionID, SequenceID);
            m_melodyList = m_root.SelectNodes(melodystring);

            ConcreteProgression baseprogression;
            
            //TODO: only grab appropriate nodes and loop through those.
            foreach (XmlNode n in m_melodyList)
            {
                melodyid = Byte.Parse(n.Attributes.GetNamedItem("MelodyID").InnerText);

                MelodyCluster cluster = new MelodyCluster(); 

                Melody m = new Melody();

                if (n.Attributes.GetNamedItem("ProgressionID") != null)
                {
                    m.MelodyID = melodyid;
                    m.CompositionID = Byte.Parse(n.Attributes.GetNamedItem("CompositionID").InnerText);
                    m.ProgressionID = Byte.Parse(n.Attributes.GetNamedItem("ProgressionID").InnerText);
                    m.SequenceID = Byte.Parse(n.Attributes.GetNamedItem("SequenceID").InnerText);

                    if (n.Attributes.GetNamedItem("Multiplier") != null)
                        m.Multiplier = Byte.Parse(n.Attributes.GetNamedItem("Multiplier").InnerText);
                    else
                        m.Multiplier = 1;

                    m_clusterList = n.ChildNodes;

                    if (m_clusterList.Count > 0)
                        LoadClusters(m);

                    m.TotalBeats *= m.Multiplier;
                }
                else
                {
                    baseprogression = Composition.Progressions.Where(p => p.CompositionID == CompositionID && p.ProgressionID == ProgressionID).First();
                    m = (Melody)baseprogression.Melodies.Where(ml => ml.MelodyID == melodyid).First().Clone();
                }

                beats += m.TotalBeats;

                Melodies.Add(m);
            }

            //TODO: Make sure the correct number of beats are associated with each progression and melody
            if (TotalBeats % beats > 0)
                throw new Exception("Invalid XML: Progression beat total is not divisible by melody beat total.");
        }

        /// <summary>
        /// Load all of the clusters for this melody
        /// </summary>
        /// <param name="m"></param>
        protected override void LoadClusters(Melody m)
        {
            m.Cluster = new MelodyCluster[m_clusterList.Count];

            int j = 0;
            int beatadder = 0;
            
            if (m_melodies.Where(ml => ml.ProgressionID == ProgressionID).Count() > 0)
                beatadder = m_melodies.Where(ml => ml.ProgressionID == ProgressionID).Select(ml => ml.TotalBeats).Sum();

            foreach (XmlNode c in m_clusterList)
            {
                MelodyCluster mc = new MelodyCluster();
                mc.ClusterID = Byte.Parse(c.Attributes.GetNamedItem("ClusterID").InnerText);
                mc.Location = m.TotalBeats + beatadder;

                if (c.Attributes.GetNamedItem("Beats") != null)
                {
                    mc.Beats = Byte.Parse(c.Attributes.GetNamedItem("Beats").InnerText);

                    if (c.Attributes.GetNamedItem("Notes") != null)
                        mc.Notes = c.Attributes.GetNamedItem("Notes").InnerText;
                    else
                        mc.Notes = m.Cluster.Where(cl => cl.ClusterID == mc.ClusterID).Select(cl => cl.Notes).First();

                    if (c.Attributes.GetNamedItem("Multiplier") != null)
                        mc.Multiplier = Byte.Parse(c.Attributes.GetNamedItem("Multiplier").InnerText);
                    else
                        mc.Multiplier = 1;
                }
                else
                {
                    mc = (MelodyCluster)m.Cluster.Where(cl => cl.ClusterID == mc.ClusterID).First().Clone();
                    mc.Location = m.TotalBeats;
                }

                m.TotalBeats += mc.Beats * mc.Multiplier;
                m.Cluster[j++] = mc;
            }
        }

        #endregion

        #region Properties

        /// <summary>
        /// Owning compisition of this progression
        /// </summary>
        public override Composition Composition
        {
            get
            {
                return m_composition;
            }
            set
            {
                m_composition = value;
            }
        }

        /// <summary>
        /// Chord dictionary for the progression - key is the chord, value is the beat on which the chord starts
        /// </summary>
        public override Dictionary<int, Chord> Chords
        {
            get
            {
                if (m_chords == null)
                    m_chords = new Dictionary<int, Chord>();

                return m_chords;
            }
        }

        /// <summary>
        /// Hyphen delimted scale degree list for display on the UI.
        /// </summary>
        public override string ScaleDegrees { get; set; }

        /// <summary>
        /// This progressions accompanying drum phrase
        /// </summary>
        public override DrumPhrase DrumPhrase
        {
            get
            {
                return m_drumphrase;
            }
            set
            {
                m_drumphrase = value;
            }
        }

        /// <summary>
        /// This progression's bass phrase
        /// </summary>
        public override BassPhrase BassPhrase
        {
            get
            {
                return m_bassPhrase;
            }
            set
            {
                m_bassPhrase = value;
            }
        }

        /// <summary>
        /// This progression's rhythm (chord playing) phrase
        /// </summary>
        public override RhythmPhrase RhythmPhrase
        {
            get
            {
                return m_rhythmPhrase;
            }
            set
            {
                m_rhythmPhrase = value;
            }
        }

        /// <summary>
        /// This progression's melody phrase
        /// </summary>
        public override MelodyPhrase MelodyPhrase
        {
            get
            {
                return m_melodyPhrase;
            }
            set
            {
                m_melodyPhrase = value;
            }
        }

        /// <summary>
        /// Amount of times the progression is repeated
        /// </summary>
        public override int Multiplier { get; set; }

        /// <summary>
        /// Total beat length for the progression
        /// </summary>
        public override int TotalBeats
        {
            get
            {
                return m_totalBeats;
            }
            set
            {
                m_totalBeats = value;
            }
        }

        /// <summary>
        /// The number of measures this progression lasts for
        /// </summary>
        public override int TotalMeasures
        {
            get
            {
                return TotalBeats / Numerator;
            }
        }

        /// <summary>
        /// Convenience property to hold all of the notes for this progression
        /// </summary>
        public override List<ConcreteNote> Notes
        {
            get
            {
                return m_notes;
            }
            set
            {
                m_notes = value;
            }
        }

        /// <summary>
        /// Melodies associated with this progression
        /// </summary>
        public override List<Melody> Melodies
        {
            get
            {
                return m_melodies;
            }
            set
            {
                m_melodies = value;
            }
        }

        /// <summary>
        /// The current measure of the current progression
        /// </summary>
        public override int CurrentMeasure
        {
            get
            {
                return m_currentMeasure;
            }
            set
            {
                m_currentMeasure = value;
            }
        }

        /// <summary>
        /// Number of beats per repeitition
        /// </summary>
        public override int BeatsPerCycle
        {
            get
            {
                return m_beatsPerCycle;
            }
            set
            {
                m_beatsPerCycle = value;
            }
        }

        /// <summary>
        /// Sequnce of this progression
        /// </summary>
        public override int SequenceID
        {
            get
            {
                return m_sequenceId;
            }
            set
            {
                m_sequenceId = value;
            }
        }

        /// <summary>
        /// Key of the current progression
        /// </summary>
        public override CircleOfFifths.Key Key { get; set; }
        #endregion
    }
}
